{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from datetime import datetime, date\n",
    "import pandas as pd\n",
    "from gs_quant.instrument import IRSwap\n",
    "from gs_quant.common import BuySell, OptionType\n",
    "from gs_quant.backtests.triggers import PeriodicTrigger, PeriodicTriggerRequirements\n",
    "from gs_quant.backtests.actions import AddTradeAction, HedgeAction\n",
    "from gs_quant.backtests.generic_engine import GenericEngine\n",
    "from gs_quant.backtests.strategy import Strategy\n",
    "from gs_quant.risk import Price, IRDelta"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Initialize session\n",
    "from gs_quant.session import GsSession\n",
    "GsSession.use(client_id=None, client_secret=None, scopes=('run_analytics',)) "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Simple backtest with no holiday calendar handling."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Define backtest dates\n",
    "start_date = date(2024, 5, 3)\n",
    "end_date = date(2024, 6, 3)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Define strategy  -- this strategy adds a swap every day holding forever.\n",
    "\n",
    "trig_req = PeriodicTriggerRequirements(start_date=start_date, end_date=end_date, frequency='1b')\n",
    "irswap = IRSwap('Pay', '10y', 'USD', notional_amount=10000, name='10y')\n",
    "actions = AddTradeAction(irswap)\n",
    "triggers = PeriodicTrigger(trig_req, actions)\n",
    "strategy = Strategy(None, triggers)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# run backtest daily\n",
    "GE = GenericEngine()\n",
    "backtest = GE.run_backtest(strategy, start=start_date, end=end_date, frequency='1b', show_progress=True)\n",
    "backtest.result_summary\n",
    "\n",
    "# Note that May 27th appears in result summary"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# we can see that a trade was opened on 27th May 2024 dispite it being a holiday because we didn't say anything about holidays in our strategy\n",
    "date(2024, 5, 27) in list(backtest.trade_ledger()['Open'])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# now change the trigger not to trigger on exclude May 27th as it was memorial day\n",
    "holiday = (date(2024, 5, 27),)\n",
    "\n",
    "trig_req_exclude_hol = PeriodicTriggerRequirements(start_date=start_date, end_date=end_date, frequency='1b', calendar=holiday)\n",
    "triggers_exc_hol = PeriodicTrigger(trig_req_exclude_hol, actions)\n",
    "strategy_exc_hol = Strategy(None, triggers_exc_hol)\n",
    "\n",
    "backtest2 = GE.run_backtest(strategy_exc_hol, start=start_date, end=end_date, frequency='1b', show_progress=True)\n",
    "backtest2.result_summary\n",
    "\n",
    "# note May 27th still appears on result summary as valuing trades is done on the schedule created by run_backtest which didn't include this holiday"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# we can see that no trade was opened on 27th May 2024 because the trigger knew it wasn't a valid day\n",
    "date(2024, 5, 27) in list(backtest2.trade_ledger()['Open'])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# now lets run the strategy above excluding holidays at the run_backtest level\n",
    "backtest3 = GE.run_backtest(strategy_exc_hol, start=start_date, end=end_date, frequency='1b', show_progress=True, holiday_calendar=holiday)\n",
    "backtest3.result_summary\n",
    "\n",
    "# now we can see that May 27th is excluded from the result_summary.  No trades were created on this date and no risks run."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Hang on.  What if the action specifies an exit date for a trade and that falls on a holiday!?\n",
    "\n",
    "actions_roll_daily = AddTradeAction(irswap, '1b')\n",
    "triggers_roll_daily = PeriodicTrigger(trig_req_exclude_hol, actions_roll_daily)\n",
    "strategy_roll_daily = Strategy(None, triggers_roll_daily)\n",
    "\n",
    "backtest4 = GE.run_backtest(strategy_roll_daily, start=start_date, end=end_date, frequency='1b', show_progress=True, holiday_calendar=holiday)\n",
    "backtest4.result_summary\n",
    "\n",
    "# now we see that May 27th is back...  That is because the action didn't know that it couldn't exit a trade on that date."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "date(2024, 5, 27) in list(backtest4.trade_ledger()['Open'])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "date(2024, 5, 27) in list(backtest4.trade_ledger()['Close'])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# so now lets tell the action that 27th May is a holiday\n",
    "\n",
    "actions_roll_daily_hol = AddTradeAction(irswap, '1b', holiday_calendar=holiday)\n",
    "triggers_roll_daily_hol = PeriodicTrigger(trig_req_exclude_hol, actions_roll_daily_hol)\n",
    "strategy_roll_daily_hol = Strategy(None, triggers_roll_daily_hol)\n",
    "\n",
    "backtest5 = GE.run_backtest(strategy_roll_daily_hol, start=start_date, end=end_date, frequency='1b', show_progress=True, holiday_calendar=holiday)\n",
    "backtest5.result_summary\n",
    "\n",
    "\n",
    "# success"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
